package com.lightjet.macross.base.feign.api.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lightjet.macross.base.feign.api.annotation.SecurityCheck;
import com.lightjet.macross.base.feign.api.domain.AuthUser;
import com.lightjet.macross.base.feign.api.dto.upms.ApiRulesDTO;
import com.lightjet.macross.base.feign.api.remoteclient.BaseUPMSRemoteClient;
//import com.lightjet.macross.base.feign.api.remoteclient.BaseUtilRemoteClient;
import com.lightjet.macross.common.dto.ResponseDTO;
import com.lightjet.macross.common.util.RespUtil;
import io.netty.util.internal.StringUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.java.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.checkerframework.checker.units.qual.A;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import static com.alibaba.druid.sql.ast.SQLPartitionValue.Operator.List;

@Aspect
@Component
@Log
public class ApiWatcherAspect {

    @Value("${spring.application.name}")
    String appName;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Resource
    BaseUPMSRemoteClient baseUtilRemoteClient;

    @Pointcut( "@annotation(io.swagger.annotations.ApiOperation)")
    //@Pointcut( "target(com.lightjet.macross.base.feign.api.service.mes.MESRemoteClient)")
    public void SWGApiOperPointcut(){}

    @Pointcut( "@annotation(com.lightjet.macross.base.feign.api.annotation.SecurityCheck)")
    //@Pointcut( "target(com.lightjet.macross.base.feign.api.service.mes.MESRemoteClient)")
    public void SecurityCheckPointcut(){}

    @Around("(SecurityCheckPointcut() &&  @annotation(securityCheck))") //指定拦截器规则；也可以直接把“execution(* com.xjj………)”写进这里
    public Object SecurityCheckInterceptor(ProceedingJoinPoint pjp, SecurityCheck securityCheck) throws Throwable {
//        MethodSignature signature = (MethodSignature) pjp.getSignature();
//        Method method = signature.getMethod(); //获取被拦截的方法
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        ResponseDTO<Map<String,Object>> res = checkApiUri(request);
        if(!res.getResult()){
            return res;
        }
        // 过滤掉鉴权api自身

        /*for(Class<?> cls: pjp.getClass().getInterfaces())
        {
            Api apiAT = cls.getAnnotation(Api.class);
            if(null != apiAT)
            {
                log.info("正在访问接口类型："+apiAT.tags());
            }

        }*/
        //Api annotation =
        return LogExecution(pjp,res.getData(),securityCheck.value());
    }

    @Around("(SWGApiOperPointcut() &&  @annotation(apiOperation))") //指定拦截器规则；也可以直接把“execution(* com.xjj………)”写进这里
    public Object SWGApiOperInterceptor(ProceedingJoinPoint pjp, ApiOperation apiOperation) throws Throwable {
        //public Object Interceptor(ProceedingJoinPoint pjp, ApiOperation apiOperation) throws Throwable {
        // MethodSignature signature = (MethodSignature) pjp.getSignature();
        //String class_name = pjp.getTarget().getClass().getName();
        // Method method = signature.getMethod(); //获取被拦截的方法
        Api api = pjp.getTarget().getClass().getAnnotation(Api.class);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if(api != null)
        {
            log.info("正在访问接口集："+ Arrays.toString(api.tags()));
        }
        if(null != apiOperation)
        {
            log.info(MessageFormat.format("正在访问接口【{0}{1}】：{2}",appName, request.getRequestURI(), apiOperation.value()));
        }
        ResponseDTO<Map<String,Object>> res = checkApiUri(request);
        if(!res.getResult()){
            return res;
        }
        // 过滤掉鉴权api自身

        /*for(Class<?> cls: pjp.getClass().getInterfaces())
        {
            Api apiAT = cls.getAnnotation(Api.class);
            if(null != apiAT)
            {
                log.info("正在访问接口类型："+apiAT.tags());
            }

        }*/
        //Api annotation =
        return LogExecution(pjp,res.getData(), apiOperation.value());
    }

    private ResponseDTO<Map<String,Object>> checkApiUri(HttpServletRequest request)
    {
        Map<String,Object> message = new HashMap<>();
        message.put("servicename",appName);
        message.put("apiuri",request.getRequestURI());
        message.put("requestip",request.getRemoteAddr());
        message.put("method",request.getMethod());
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        AuthUser authUser = null;
        Object clientid = null;
        if(authentication != null)
        {
            // 尝试获取clientid
            clientid =((Map)((OAuth2Authentication) authentication).getUserAuthentication().getDetails()).get("client_id");
            message.put("clientid",clientid==null?"":clientid.toString());
            // 尝试获取用户信息

            if(authentication.getPrincipal() instanceof AuthUser)
            {
                authUser = ((AuthUser) authentication.getPrincipal());
            }
        }
        else
        {
            message.put("clientid","");
            message.put("userkey","");
            message.put("userdes","");
        }

        message.put("userkey",authUser==null?"":authUser.getId());
        message.put("userdes",authUser==null?"":authUser.getDisplayname());

        if(!"base-upms/api/apirules/uri".equals((appName + request.getRequestURI()).toLowerCase())){
            ApiRulesDTO apiRulesDTO = baseUtilRemoteClient.GetRuleByPath(appName,request.getRequestURI()).getData();
            if(apiRulesDTO != null){ // 有验证要求

                if(authentication == null) {

                    message.put("content","鉴权失败[无Authentication信息],当前客户端及用户信息无访问权限");
                    message.put("result",1);
                    return RespUtil.CreateError(message.get("content").toString(),message);
                }
                else
                {

                    // 规则对clientid 有要求
                    if(!StringUtil.isNullOrEmpty(apiRulesDTO.getClients()))
                    {
                        if(clientid!=null){
                            String clientidFinal = clientid.toString();
                            if(!Arrays.stream(apiRulesDTO.getClients().toString().split(",")).anyMatch(x-> clientidFinal.toString().equals(x)))
                            {
                                message.put("content","鉴权失败[ClientID不匹配],当前客户端及用户信息无访问权限");
                                message.put("result",1);
                                return RespUtil.CreateError(message.get("content").toString(),message);
                            }
                        }
                        else
                        {
                            message.put("content","鉴权失败[无ClientID信息],当前客户端及用户信息无访问权限");
                            message.put("result",1);
                            return RespUtil.CreateError(message.get("content").toString(),message);
                        }
                    }


                    // 规则对角色有要求
                    if(!StringUtil.isNullOrEmpty(apiRulesDTO.getRoles()))
                    {
                        if (authUser!= null) {
                            // 获取当前请求用户的角色
                            if (authUser.getAuthorities() != null) {
                                for (String role : apiRulesDTO.getRoles().split(",")) {
                                    if (!authUser.getAuthorities().stream().anyMatch(x -> ((GrantedAuthority) x).getAuthority().toString().toUpperCase().equals("ROLE_" + role))) {
                                        message.put("content","鉴权失败[用户角色不匹配],当前客户端及用户信息无访问权限");
                                        message.put("result",1);
                                        return RespUtil.CreateError(message.get("content").toString(),message);
                                    }
                                }
                            } else {
                                message.put("content","鉴权失败[无用户角色信息],当前客户端及用户信息无访问权限");
                                message.put("result",1);
                                return RespUtil.CreateError(message.get("content").toString(),message);
                            }
                        }
                        else
                        {
                            message.put("content","鉴权失败[无AuthUser信息],当前客户端及用户信息无访问权限");
                            message.put("result",1);
                            return RespUtil.CreateError(message.get("content").toString(),message);
                        }
                    }

                    log.info("API【"+appName + request.getRequestURI()+"】鉴权成功");
                    return RespUtil.CreateOK(message);


                }

//                if(request.getUserPrincipal() == null){
//                    return RespUtil.CreateError("鉴权失败[无UserPrincipa信息],当前客户端及用户信息无访问权限");
//                }
//                else {
//                    if (request.getUserPrincipal() instanceof OAuth2Authentication)
//                    {
//                        //((OAuth2Authentication)request.getUserPrincipal()).get
//                        return RespUtil.CreateOK();
//                    }
//                    else
//                    {
//                        return RespUtil.CreateOK();
//                    }
//                }
            }
            return RespUtil.CreateOK(message);
        }
        message.put("clientid","");
        message.put("userkey","");
        message.put("userdes","");
        return RespUtil.CreateOK(message);
    }

    // 记录接口处理日志
    private Object LogExecution(ProceedingJoinPoint pjp,Map<String,Object> message,String caption){

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod(); //获取被拦截的方法
        String methodName = method.getName(); //获取被拦截的方法名
        message.put("caption",caption);
        message.put("invokeclass",method.getDeclaringClass().getName());
        message.put("invokemethod",methodName);

        long beginTime = System.currentTimeMillis();
        Object result = null;
        try {
            message.put("paramtypes",new ObjectMapper().writeValueAsString(method.getParameterTypes()));
            message.put("paramvalues",new ObjectMapper().writeValueAsString(pjp.getArgs()));
            result = pjp.proceed();
            message.put("costtime",System.currentTimeMillis() - beginTime);
            if(result instanceof ResponseDTO)
            {
                //业务处理成功
                if(((ResponseDTO) result).getResult()){
                    message.put("loglevel",0);
                    message.put("content",  new ObjectMapper().writeValueAsString(result));
                    message.put("result",1);
                }
                else //业务处理失败
                {
                    message.put("result",0);
                    message.put("loglevel",1);
                }
            }
            else
            {
                message.put("loglevel",0);
                message.put("result",1);
            }
        }
        catch (Throwable throwable){
            message.put("costtime",System.currentTimeMillis() - beginTime);
            message.put("loglevel",2);
            if(!StringUtil.isNullOrEmpty(throwable.getMessage()))
            {
                message.put("content",throwable.getMessage());
            }
            else
            {
                message.put("content","失败");
            }
            result = RespUtil.CreateError( message.get("content").toString());
            // log.info(MessageFormat.format("响应【{0}】请求结束，结果:{1}，耗时：{2}ms",methodName,"失败", costMs));

        }
        try
        {
            rabbitTemplate.convertAndSend("ApiExecLogExchange","macross.apiexec.log",message);
        }
        catch (Exception e)
        {
            log.info("发送消息队列失败："+ e.getMessage());
        }
        return result;
    }
}
